// TcpServer.hpp
#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// #include <pthread.h>
// #include <signal.h>
#include "log.hpp"
// #include "Task.hpp"
// #include "threadpool.hpp"
// #include "daemon.hpp"

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 1; // 一般不要设置得太大

enum
{
    UsageError = 1,
    SockerError,
    BindError,
    ListenError
};

class TcpServer;  // 类声明

// class ThreadData
// {
// public:
//     ThreadData(int fd, const std::string &ip, uint16_t port, TcpServer *t)
//         :sockfd(fd), clientip(ip), clientport(port), tsvr(t)
//     {}
// public:
//     int sockfd;
//     std::string clientip;
//     uint16_t clientport;
//     TcpServer *tsvr;  // 代替this指针
// };

class TcpServer
{
public:
    TcpServer(uint16_t port, const std::string &ip = defaultip)
        : _listensock(defaultfd), _port(port), _ip(ip)
    {
    }
    void InitServer()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            lg(Fatal, "create socket error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(SockerError);
        }
        lg(Info, "create socket success, _listensock: %d", _listensock);

        // 防止偶发性的服务器无法进行立即重启（TCP协议时解释）
        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        inet_aton(_ip.c_str(), &local.sin_addr);
        // local.sin_addr.s_addr = INADDR_ANY;

        if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind success, _listensock: %d", _listensock);

        // TCP是面向连接的，服务器一般是比较“被动”的，所以服务器需要一直处于一种等待连接到来的状态
        if (listen(_listensock, backlog) < 0)
        {
            lg(Fatal, "listen error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "listen success, _listensock: %d", _listensock);
    }
    void RunServer()
    {
        // lg.Enable(Classfile);
        // Daemon();
        // signal(SIGCHLD, SIG_IGN);  // 默认不用等待子进程，子进程直接被系统回收
        // signal(SIGPIPE, SIG_IGN);  // 防止write时向已释放的套接字写入导致程序收到信号退出
        // ThreadPool<Task>::GetInstance()->Start();  // 启动线程池

        lg(Info, "tcpServer is running...");
        for (;;)
        {
            sleep(1);
            // 1.获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(_listensock, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                lg(Warning, "accept error, errno: %d, strerror: %s", errno, strerror(errno));
                continue;
            }

            uint16_t clientport = ntohs(client.sin_port);
            char clientip[16];
            inet_ntop(AF_INET, &client.sin_addr, clientip, sizeof(clientip));
            lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);

            sleep(5);
            close(sockfd);
            lg(Info, "close sockfd: %d", sockfd);

            // 2.根据新连接来进行通信
            // version1 -- 单进程版，同一时间只能为一个客户端服务
            // Service(sockfd, clientip, clientport);
            // close(sockfd);

            // version2 -- 多进程版，可以同时为多个客户端服务
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     // child
            //     close(_listensock);  // 子进程不需要这个监听套接字，直接关闭
            //     if (fork() > 0) exit(0);  // 子进程退出
            //     // 孙子进程，由系统领养
            //     Service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // // father
            // close(sockfd);  // 通信任务交给了子进程，父进程不需要再关心这个通信套接字，必须关闭
            // pid_t rid = waitpid(id, nullptr, 0);

            // version3 -- 多线程版
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            // // version4 -- 线程池版本
            // Task t(sockfd, clientip, clientport);
            // ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
    // static void *Routine(void *args)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData *td = static_cast<ThreadData*>(args);
    //     td->tsvr->Service(td->sockfd, td->clientip, td->clientport);  // 静态成员函数必须通过this指针访问类内成员
    //     close(td->sockfd);
    //     delete td;
    //     return nullptr;
    // }
    // void Service(int sockfd, const std::string &clientip, uint16_t clientport)
    // {
    //     char buffer[4096];
    //     while (true)
    //     {
    //         ssize_t n = read(sockfd, buffer, sizeof(buffer));
    //         if (n < 0)
    //         {
    //             lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //         else if (n == 0)
    //         {
    //             lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         buffer[n] = 0;
    //         std::cout << "client say# " << buffer << std::endl;

    //         std::string echo_string = "tcpserver echo# ";
    //         echo_string += buffer;
    //         int m = write(sockfd, echo_string.c_str(), echo_string.size());
    //         if (m < 0)
    //         {
    //             lg(Warning, "write error, errno: %d, strerror: %s", errno, strerror(errno));
    //         }
    //     }
    // }
    ~TcpServer() {}

private:
    int _listensock;
    uint16_t _port;
    std::string _ip;
};